<!doctype html>

<html>
<head>
  <link rel="shortcut icon" href="static/images/favicon.ico" type="image/x-icon">
  <title>seamlessfield.js (Closure Library API Documentation - JavaScript)</title>
  <link rel="stylesheet" href="static/css/base.css">
  <link rel="stylesheet" href="static/css/doc.css">
  <link rel="stylesheet" href="static/css/sidetree.css">
  <link rel="stylesheet" href="static/css/prettify.css">

  <script>
     var _staticFilePath = "static/";
     var _typeTreeName = "goog";
     var _fileTreeName = "Source";
  </script>

  <script src="static/js/doc.js">
  </script>


  <meta charset="utf8">
</head>

<body onload="grokdoc.onLoad();">

<div id="header">
  <div class="g-section g-tpl-50-50 g-split">
    <div class="g-unit g-first">
      <a id="logo" href="index.html">Closure Library API Documentation</a>
    </div>

    <div class="g-unit">
      <div class="g-c">
        <strong>Go to class or file:</strong>
        <input type="text" id="ac">
      </div>
    </div>
  </div>
</div>

<div class="clear"></div>

<h2><a href="closure_goog_editor_seamlessfield.js.html">seamlessfield.js</a></h2>

<pre class="prettyprint lang-js">
<a name="line1"></a>// Copyright 2006 The Closure Library Authors. All Rights Reserved.
<a name="line2"></a>//
<a name="line3"></a>// Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
<a name="line4"></a>// you may not use this file except in compliance with the License.
<a name="line5"></a>// You may obtain a copy of the License at
<a name="line6"></a>//
<a name="line7"></a>//      http://www.apache.org/licenses/LICENSE-2.0
<a name="line8"></a>//
<a name="line9"></a>// Unless required by applicable law or agreed to in writing, software
<a name="line10"></a>// distributed under the License is distributed on an &quot;AS-IS&quot; BASIS,
<a name="line11"></a>// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
<a name="line12"></a>// See the License for the specific language governing permissions and
<a name="line13"></a>// limitations under the License.
<a name="line14"></a>
<a name="line15"></a>/**
<a name="line16"></a> * @fileoverview Class to encapsulate an editable field that blends in with
<a name="line17"></a> * the style of the page. The field can be fixed height, grow with its
<a name="line18"></a> * contents, or have a min height after which it grows to its contents.
<a name="line19"></a> * This is a goog.editor.Field, but with blending and sizing capabilities,
<a name="line20"></a> * and avoids using an iframe whenever possible.
<a name="line21"></a> *
<a name="line22"></a> * @see ../demos/editor/seamlessfield.html
<a name="line23"></a> */
<a name="line24"></a>
<a name="line25"></a>
<a name="line26"></a>goog.provide(&#39;goog.editor.SeamlessField&#39;);
<a name="line27"></a>
<a name="line28"></a>goog.require(&#39;goog.cssom.iframe.style&#39;);
<a name="line29"></a>goog.require(&#39;goog.debug.Logger&#39;);
<a name="line30"></a>goog.require(&#39;goog.dom&#39;);
<a name="line31"></a>goog.require(&#39;goog.dom.Range&#39;);
<a name="line32"></a>goog.require(&#39;goog.dom.TagName&#39;);
<a name="line33"></a>goog.require(&#39;goog.editor.BrowserFeature&#39;);
<a name="line34"></a>goog.require(&#39;goog.editor.Field&#39;);
<a name="line35"></a>goog.require(&#39;goog.editor.icontent&#39;);
<a name="line36"></a>goog.require(&#39;goog.editor.icontent.FieldFormatInfo&#39;);
<a name="line37"></a>goog.require(&#39;goog.editor.icontent.FieldStyleInfo&#39;);
<a name="line38"></a>goog.require(&#39;goog.editor.node&#39;);
<a name="line39"></a>goog.require(&#39;goog.events&#39;);
<a name="line40"></a>goog.require(&#39;goog.events.EventType&#39;);
<a name="line41"></a>goog.require(&#39;goog.style&#39;);
<a name="line42"></a>
<a name="line43"></a>
<a name="line44"></a>
<a name="line45"></a>/**
<a name="line46"></a> * This class encapsulates an editable field that blends in with the
<a name="line47"></a> * surrounding page.
<a name="line48"></a> * To see events fired by this object, please see the base class.
<a name="line49"></a> *
<a name="line50"></a> * @param {string} id An identifer for the field. This is used to find the
<a name="line51"></a> *     field and the element associated with this field.
<a name="line52"></a> * @param {Document=} opt_doc The document that the element with the given
<a name="line53"></a> *     id can be found it.
<a name="line54"></a> * @constructor
<a name="line55"></a> * @extends {goog.editor.Field}
<a name="line56"></a> */
<a name="line57"></a>goog.editor.SeamlessField = function(id, opt_doc) {
<a name="line58"></a>  goog.editor.Field.call(this, id, opt_doc);
<a name="line59"></a>};
<a name="line60"></a>goog.inherits(goog.editor.SeamlessField, goog.editor.Field);
<a name="line61"></a>
<a name="line62"></a>
<a name="line63"></a>/**
<a name="line64"></a> * @override
<a name="line65"></a> */
<a name="line66"></a>goog.editor.SeamlessField.prototype.logger =
<a name="line67"></a>    goog.debug.Logger.getLogger(&#39;goog.editor.SeamlessField&#39;);
<a name="line68"></a>
<a name="line69"></a>// Functions dealing with field sizing.
<a name="line70"></a>
<a name="line71"></a>
<a name="line72"></a>/**
<a name="line73"></a> * The key used for listening for the &quot;dragover&quot; event.
<a name="line74"></a> * @type {goog.events.Key}
<a name="line75"></a> * @private
<a name="line76"></a> */
<a name="line77"></a>goog.editor.SeamlessField.prototype.listenForDragOverEventKey_;
<a name="line78"></a>
<a name="line79"></a>
<a name="line80"></a>/**
<a name="line81"></a> * The key used for listening for the iframe &quot;load&quot; event.
<a name="line82"></a> * @type {goog.events.Key}
<a name="line83"></a> * @private
<a name="line84"></a> */
<a name="line85"></a>goog.editor.SeamlessField.prototype.listenForIframeLoadEventKey_;
<a name="line86"></a>
<a name="line87"></a>
<a name="line88"></a>/**
<a name="line89"></a> * Sets the min height of this editable field&#39;s iframe. Only used in growing
<a name="line90"></a> * mode when an iframe is used. This will cause an immediate field sizing to
<a name="line91"></a> * update the field if necessary based on the new min height.
<a name="line92"></a> * @param {number} height The min height specified as a number of pixels,
<a name="line93"></a> *    e.g., 75.
<a name="line94"></a> */
<a name="line95"></a>goog.editor.SeamlessField.prototype.setMinHeight = function(height) {
<a name="line96"></a>  if (height == this.minHeight_) {
<a name="line97"></a>    // Do nothing if the min height isn&#39;t changing.
<a name="line98"></a>    return;
<a name="line99"></a>  }
<a name="line100"></a>  this.minHeight_ = height;
<a name="line101"></a>  if (this.usesIframe()) {
<a name="line102"></a>    this.doFieldSizingGecko();
<a name="line103"></a>  }
<a name="line104"></a>};
<a name="line105"></a>
<a name="line106"></a>
<a name="line107"></a>/**
<a name="line108"></a> * Whether the field should be rendered with a fixed height, or should expand
<a name="line109"></a> * to fit its contents.
<a name="line110"></a> * @type {boolean}
<a name="line111"></a> * @private
<a name="line112"></a> */
<a name="line113"></a>goog.editor.SeamlessField.prototype.isFixedHeight_ = false;
<a name="line114"></a>
<a name="line115"></a>
<a name="line116"></a>/**
<a name="line117"></a> * Whether the fixed-height handling has been overridden manually.
<a name="line118"></a> * @type {boolean}
<a name="line119"></a> * @private
<a name="line120"></a> */
<a name="line121"></a>goog.editor.SeamlessField.prototype.isFixedHeightOverridden_ = false;
<a name="line122"></a>
<a name="line123"></a>
<a name="line124"></a>/**
<a name="line125"></a> * @return {boolean} Whether the field should be rendered with a fixed
<a name="line126"></a> *    height, or should expand to fit its contents.
<a name="line127"></a> * @override
<a name="line128"></a> */
<a name="line129"></a>goog.editor.SeamlessField.prototype.isFixedHeight = function() {
<a name="line130"></a>  return this.isFixedHeight_;
<a name="line131"></a>};
<a name="line132"></a>
<a name="line133"></a>
<a name="line134"></a>/**
<a name="line135"></a> * @param {boolean} newVal Explicitly set whether the field should be
<a name="line136"></a> *    of a fixed-height. This overrides auto-detection.
<a name="line137"></a> */
<a name="line138"></a>goog.editor.SeamlessField.prototype.overrideFixedHeight = function(newVal) {
<a name="line139"></a>  this.isFixedHeight_ = newVal;
<a name="line140"></a>  this.isFixedHeightOverridden_ = true;
<a name="line141"></a>};
<a name="line142"></a>
<a name="line143"></a>
<a name="line144"></a>/**
<a name="line145"></a> * Auto-detect whether the current field should have a fixed height.
<a name="line146"></a> * @private
<a name="line147"></a> */
<a name="line148"></a>goog.editor.SeamlessField.prototype.autoDetectFixedHeight_ = function() {
<a name="line149"></a>  if (!this.isFixedHeightOverridden_) {
<a name="line150"></a>    var originalElement = this.getOriginalElement();
<a name="line151"></a>    if (originalElement) {
<a name="line152"></a>      this.isFixedHeight_ =
<a name="line153"></a>          goog.style.getComputedOverflowY(originalElement) == &#39;auto&#39;;
<a name="line154"></a>    }
<a name="line155"></a>  }
<a name="line156"></a>};
<a name="line157"></a>
<a name="line158"></a>
<a name="line159"></a>/**
<a name="line160"></a> * Resize the iframe in response to the wrapper div changing size.
<a name="line161"></a> * @private
<a name="line162"></a> */
<a name="line163"></a>goog.editor.SeamlessField.prototype.handleOuterDocChange_ = function() {
<a name="line164"></a>  if (this.isEventStopped(goog.editor.Field.EventType.CHANGE)) {
<a name="line165"></a>    return;
<a name="line166"></a>  }
<a name="line167"></a>  this.sizeIframeToWrapperGecko_();
<a name="line168"></a>};
<a name="line169"></a>
<a name="line170"></a>
<a name="line171"></a>/**
<a name="line172"></a> * Sizes the iframe to its body&#39;s height.
<a name="line173"></a> * @private
<a name="line174"></a> */
<a name="line175"></a>goog.editor.SeamlessField.prototype.sizeIframeToBodyHeightGecko_ = function() {
<a name="line176"></a>  if (this.acquireSizeIframeLockGecko_()) {
<a name="line177"></a>    var resized = false;
<a name="line178"></a>    var ifr = this.getEditableIframe();
<a name="line179"></a>    if (ifr) {
<a name="line180"></a>      var fieldHeight = this.getIframeBodyHeightGecko_();
<a name="line181"></a>
<a name="line182"></a>      if (this.minHeight_) {
<a name="line183"></a>        fieldHeight = Math.max(fieldHeight, this.minHeight_);
<a name="line184"></a>      }
<a name="line185"></a>      if (parseInt(goog.style.getStyle(ifr, &#39;height&#39;), 10) != fieldHeight) {
<a name="line186"></a>        ifr.style.height = fieldHeight + &#39;px&#39;;
<a name="line187"></a>        resized = true;
<a name="line188"></a>      }
<a name="line189"></a>    }
<a name="line190"></a>    this.releaseSizeIframeLockGecko_();
<a name="line191"></a>    if (resized) {
<a name="line192"></a>      this.dispatchEvent(goog.editor.Field.EventType.IFRAME_RESIZED);
<a name="line193"></a>    }
<a name="line194"></a>  }
<a name="line195"></a>};
<a name="line196"></a>
<a name="line197"></a>
<a name="line198"></a>/**
<a name="line199"></a> * @return {number} The height of the editable iframe&#39;s body.
<a name="line200"></a> * @private
<a name="line201"></a> */
<a name="line202"></a>goog.editor.SeamlessField.prototype.getIframeBodyHeightGecko_ = function() {
<a name="line203"></a>  var ifr = this.getEditableIframe();
<a name="line204"></a>  var body = ifr.contentDocument.body;
<a name="line205"></a>  var htmlElement = body.parentNode;
<a name="line206"></a>
<a name="line207"></a>
<a name="line208"></a>  // If the iframe&#39;s height is 0, then the offsetHeight/scrollHeight of the
<a name="line209"></a>  // HTML element in the iframe can be totally wack (i.e. too large
<a name="line210"></a>  // by 50-500px). Also, in standard&#39;s mode the clientHeight is 0.
<a name="line211"></a>  if (parseInt(goog.style.getStyle(ifr, &#39;height&#39;), 10) === 0) {
<a name="line212"></a>    goog.style.setStyle(ifr, &#39;height&#39;, 1 + &#39;px&#39;);
<a name="line213"></a>  }
<a name="line214"></a>
<a name="line215"></a>  var fieldHeight;
<a name="line216"></a>  if (goog.editor.node.isStandardsMode(body)) {
<a name="line217"></a>
<a name="line218"></a>    // If in standards-mode,
<a name="line219"></a>    // grab the HTML element as it will contain all the field&#39;s
<a name="line220"></a>    // contents. The body&#39;s height, for example, will not include that of
<a name="line221"></a>    // floated images at the bottom in standards mode.
<a name="line222"></a>    // Note that this value include all scrollbars *except* for scrollbars
<a name="line223"></a>    // on the HTML element itself.
<a name="line224"></a>    fieldHeight = htmlElement.offsetHeight;
<a name="line225"></a>  } else {
<a name="line226"></a>    // In quirks-mode, the body-element always seems
<a name="line227"></a>    // to size to the containing window.  The html-element however,
<a name="line228"></a>    // sizes to the content, and can thus end up with a value smaller
<a name="line229"></a>    // than its child body-element if the content is shrinking.
<a name="line230"></a>    // We want to make the iframe shrink too when the content shrinks,
<a name="line231"></a>    // so rather than size the iframe to the body-element, size it to
<a name="line232"></a>    // the html-element.
<a name="line233"></a>    fieldHeight = htmlElement.scrollHeight;
<a name="line234"></a>
<a name="line235"></a>    // If there is a horizontal scroll, add in the thickness of the
<a name="line236"></a>    // scrollbar.
<a name="line237"></a>    if (htmlElement.clientHeight != htmlElement.offsetHeight) {
<a name="line238"></a>      fieldHeight += goog.editor.SeamlessField.getScrollbarWidth_();
<a name="line239"></a>    }
<a name="line240"></a>  }
<a name="line241"></a>
<a name="line242"></a>  return fieldHeight;
<a name="line243"></a>};
<a name="line244"></a>
<a name="line245"></a>
<a name="line246"></a>/**
<a name="line247"></a> * Grabs the width of a scrollbar from the browser and caches the result.
<a name="line248"></a> * @return {number} The scrollbar width in pixels.
<a name="line249"></a> * @private
<a name="line250"></a> */
<a name="line251"></a>goog.editor.SeamlessField.getScrollbarWidth_ = function() {
<a name="line252"></a>  return goog.editor.SeamlessField.scrollbarWidth_ ||
<a name="line253"></a>      (goog.editor.SeamlessField.scrollbarWidth_ =
<a name="line254"></a>          goog.style.getScrollbarWidth());
<a name="line255"></a>};
<a name="line256"></a>
<a name="line257"></a>
<a name="line258"></a>/**
<a name="line259"></a> * Sizes the iframe to its container div&#39;s width. The width of the div
<a name="line260"></a> * is controlled by its containing context, not by its contents.
<a name="line261"></a> * if it extends outside of it&#39;s contents, then it gets a horizontal scroll.
<a name="line262"></a> * @private
<a name="line263"></a> */
<a name="line264"></a>goog.editor.SeamlessField.prototype.sizeIframeToWrapperGecko_ = function() {
<a name="line265"></a>  if (this.acquireSizeIframeLockGecko_()) {
<a name="line266"></a>    var ifr = this.getEditableIframe();
<a name="line267"></a>    var field = this.getElement();
<a name="line268"></a>    var resized = false;
<a name="line269"></a>    if (ifr &amp;&amp; field) {
<a name="line270"></a>      var fieldPaddingBox;
<a name="line271"></a>      var widthDiv = ifr.parentNode;
<a name="line272"></a>
<a name="line273"></a>      var width = widthDiv.offsetWidth;
<a name="line274"></a>      if (parseInt(goog.style.getStyle(ifr, &#39;width&#39;), 10) != width) {
<a name="line275"></a>        fieldPaddingBox = goog.style.getPaddingBox(field);
<a name="line276"></a>        ifr.style.width = width + &#39;px&#39;;
<a name="line277"></a>        field.style.width =
<a name="line278"></a>            width - fieldPaddingBox.left - fieldPaddingBox.right + &#39;px&#39;;
<a name="line279"></a>        resized = true;
<a name="line280"></a>      }
<a name="line281"></a>
<a name="line282"></a>      var height = widthDiv.offsetHeight;
<a name="line283"></a>      if (this.isFixedHeight() &amp;&amp;
<a name="line284"></a>          parseInt(goog.style.getStyle(ifr, &#39;height&#39;), 10) != height) {
<a name="line285"></a>        if (!fieldPaddingBox) {
<a name="line286"></a>          fieldPaddingBox = goog.style.getPaddingBox(field);
<a name="line287"></a>        }
<a name="line288"></a>        ifr.style.height = height + &#39;px&#39;;
<a name="line289"></a>        field.style.height =
<a name="line290"></a>            height - fieldPaddingBox.top - fieldPaddingBox.bottom + &#39;px&#39;;
<a name="line291"></a>        resized = true;
<a name="line292"></a>      }
<a name="line293"></a>
<a name="line294"></a>    }
<a name="line295"></a>    this.releaseSizeIframeLockGecko_();
<a name="line296"></a>    if (resized) {
<a name="line297"></a>      this.dispatchEvent(goog.editor.Field.EventType.IFRAME_RESIZED);
<a name="line298"></a>    }
<a name="line299"></a>  }
<a name="line300"></a>};
<a name="line301"></a>
<a name="line302"></a>
<a name="line303"></a>/**
<a name="line304"></a> * Perform all the sizing immediately.
<a name="line305"></a> */
<a name="line306"></a>goog.editor.SeamlessField.prototype.doFieldSizingGecko = function() {
<a name="line307"></a>  // Because doFieldSizingGecko can be called after a setTimeout
<a name="line308"></a>  // it is possible that the field has been destroyed before this call
<a name="line309"></a>  // to do the sizing is executed. Check for field existence and do nothing
<a name="line310"></a>  // if it has already been destroyed.
<a name="line311"></a>  if (this.getElement()) {
<a name="line312"></a>    // The order of operations is important here.  Sizing the iframe to the
<a name="line313"></a>    // wrapper could cause the width to change, which could change the line
<a name="line314"></a>    // wrapping, which could change the body height.  So we need to do that
<a name="line315"></a>    // first, then size the iframe to fit the body height.
<a name="line316"></a>    this.sizeIframeToWrapperGecko_();
<a name="line317"></a>    if (!this.isFixedHeight()) {
<a name="line318"></a>      this.sizeIframeToBodyHeightGecko_();
<a name="line319"></a>    }
<a name="line320"></a>  }
<a name="line321"></a>};
<a name="line322"></a>
<a name="line323"></a>
<a name="line324"></a>/**
<a name="line325"></a> * Acquires a lock on resizing the field iframe. This is used to ensure that
<a name="line326"></a> * modifications we make while in a mutation event handler don&#39;t cause
<a name="line327"></a> * infinite loops.
<a name="line328"></a> * @return {boolean} False if the lock is already acquired.
<a name="line329"></a> * @private
<a name="line330"></a> */
<a name="line331"></a>goog.editor.SeamlessField.prototype.acquireSizeIframeLockGecko_ = function() {
<a name="line332"></a>  if (this.sizeIframeLock_) {
<a name="line333"></a>    return false;
<a name="line334"></a>  }
<a name="line335"></a>  return this.sizeIframeLock_ = true;
<a name="line336"></a>};
<a name="line337"></a>
<a name="line338"></a>
<a name="line339"></a>/**
<a name="line340"></a> * Releases a lock on resizing the field iframe. This is used to ensure that
<a name="line341"></a> * modifications we make while in a mutation event handler don&#39;t cause
<a name="line342"></a> * infinite loops.
<a name="line343"></a> * @private
<a name="line344"></a> */
<a name="line345"></a>goog.editor.SeamlessField.prototype.releaseSizeIframeLockGecko_ = function() {
<a name="line346"></a>  this.sizeIframeLock_ = false;
<a name="line347"></a>};
<a name="line348"></a>
<a name="line349"></a>
<a name="line350"></a>// Functions dealing with blending in with the surrounding page.
<a name="line351"></a>
<a name="line352"></a>
<a name="line353"></a>/**
<a name="line354"></a> * String containing the css rules that, if applied to a document&#39;s body,
<a name="line355"></a> * would style that body as if it were the original element we made editable.
<a name="line356"></a> * See goog.cssom.iframe.style.getElementContext for more details.
<a name="line357"></a> * @type {string}
<a name="line358"></a> * @private
<a name="line359"></a> */
<a name="line360"></a>goog.editor.SeamlessField.prototype.iframeableCss_ = &#39;&#39;;
<a name="line361"></a>
<a name="line362"></a>
<a name="line363"></a>/**
<a name="line364"></a> * Gets the css rules that should be used to style an iframe&#39;s body as if it
<a name="line365"></a> * were the original element that we made editable.
<a name="line366"></a> * @param {boolean=} opt_forceRegeneration Set to true to not read the cached
<a name="line367"></a> * copy and instead completely regenerate the css rules.
<a name="line368"></a> * @return {string} The string containing the css rules to use.
<a name="line369"></a> */
<a name="line370"></a>goog.editor.SeamlessField.prototype.getIframeableCss = function(
<a name="line371"></a>    opt_forceRegeneration) {
<a name="line372"></a>  if (!this.iframeableCss_ || opt_forceRegeneration) {
<a name="line373"></a>    var originalElement = this.getOriginalElement();
<a name="line374"></a>    if (originalElement) {
<a name="line375"></a>      this.iframeableCss_ =
<a name="line376"></a>          goog.cssom.iframe.style.getElementContext(originalElement,
<a name="line377"></a>          opt_forceRegeneration);
<a name="line378"></a>    }
<a name="line379"></a>  }
<a name="line380"></a>  return this.iframeableCss_;
<a name="line381"></a>};
<a name="line382"></a>
<a name="line383"></a>
<a name="line384"></a>/**
<a name="line385"></a> * Sets the css rules that should be used inside the editable iframe.
<a name="line386"></a> * Note: to clear the css cache between makeNotEditable/makeEditable,
<a name="line387"></a> * call this with &quot;&quot; as iframeableCss.
<a name="line388"></a> * TODO(user): Unify all these css setting methods + Nick&#39;s open
<a name="line389"></a> * CL.  This is getting ridiculous.
<a name="line390"></a> * @param {string} iframeableCss String containing the css rules to use.
<a name="line391"></a> */
<a name="line392"></a>goog.editor.SeamlessField.prototype.setIframeableCss = function(iframeableCss) {
<a name="line393"></a>  this.iframeableCss_ = iframeableCss;
<a name="line394"></a>};
<a name="line395"></a>
<a name="line396"></a>
<a name="line397"></a>/**
<a name="line398"></a> * Used to ensure that CSS stylings are only installed once for none
<a name="line399"></a> * iframe seamless mode.
<a name="line400"></a> * TODO(user): Make it a formal part of the API that you can only
<a name="line401"></a> * set one set of styles globally.
<a name="line402"></a> * In seamless, non-iframe mode, all the stylings would go in the
<a name="line403"></a> * same document and conflict.
<a name="line404"></a> * @type {boolean}
<a name="line405"></a> * @private
<a name="line406"></a> */
<a name="line407"></a>goog.editor.SeamlessField.haveInstalledCss_ = false;
<a name="line408"></a>
<a name="line409"></a>
<a name="line410"></a>/**
<a name="line411"></a> * Applies CSS from the wrapper-div to the field iframe.
<a name="line412"></a> */
<a name="line413"></a>goog.editor.SeamlessField.prototype.inheritBlendedCSS = function() {
<a name="line414"></a>  // No-op if the field isn&#39;t using an iframe.
<a name="line415"></a>  if (!this.usesIframe()) {
<a name="line416"></a>    return;
<a name="line417"></a>  }
<a name="line418"></a>  var field = this.getElement();
<a name="line419"></a>  var head = goog.dom.getDomHelper(field).getElementsByTagNameAndClass(
<a name="line420"></a>      &#39;head&#39;)[0];
<a name="line421"></a>  if (head) {
<a name="line422"></a>    // We created this &lt;head&gt;, and we know the only thing we put in there
<a name="line423"></a>    // is a &lt;style&gt; block.  So it&#39;s safe to blow away all the children
<a name="line424"></a>    // as part of rewriting the styles.
<a name="line425"></a>    goog.dom.removeChildren(head);
<a name="line426"></a>  }
<a name="line427"></a>
<a name="line428"></a>  // Force a cache-clearing in CssUtil - this function was called because
<a name="line429"></a>  // we&#39;re applying the &#39;blend&#39; for the first time, or because we
<a name="line430"></a>  // *need* to recompute the blend.
<a name="line431"></a>  var newCSS = this.getIframeableCss(true);
<a name="line432"></a>  goog.style.installStyles(newCSS, field);
<a name="line433"></a>};
<a name="line434"></a>
<a name="line435"></a>
<a name="line436"></a>// Overridden methods.
<a name="line437"></a>
<a name="line438"></a>
<a name="line439"></a>/** @override */
<a name="line440"></a>goog.editor.SeamlessField.prototype.usesIframe = function() {
<a name="line441"></a>  // TODO(user): Switch Firefox to using contentEditable
<a name="line442"></a>  // rather than designMode iframe once contentEditable support
<a name="line443"></a>  // is less buggy.
<a name="line444"></a>  return !goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE;
<a name="line445"></a>};
<a name="line446"></a>
<a name="line447"></a>
<a name="line448"></a>/** @override */
<a name="line449"></a>goog.editor.SeamlessField.prototype.setupMutationEventHandlersGecko =
<a name="line450"></a>    function() {
<a name="line451"></a>  goog.editor.SeamlessField.superClass_.setupMutationEventHandlersGecko.call(
<a name="line452"></a>      this);
<a name="line453"></a>
<a name="line454"></a>  if (this.usesIframe()) {
<a name="line455"></a>    var iframe = this.getEditableIframe();
<a name="line456"></a>    var outerDoc = iframe.ownerDocument;
<a name="line457"></a>    this.eventRegister.listen(outerDoc,
<a name="line458"></a>        goog.editor.Field.MUTATION_EVENTS_GECKO,
<a name="line459"></a>        this.handleOuterDocChange_, true);
<a name="line460"></a>
<a name="line461"></a>    // If the images load after we do the initial sizing, then this will
<a name="line462"></a>    // force a field resize.
<a name="line463"></a>    this.listenForIframeLoadEventKey_ = goog.events.listenOnce(
<a name="line464"></a>        this.getEditableDomHelper().getWindow(),
<a name="line465"></a>        goog.events.EventType.LOAD, this.sizeIframeToBodyHeightGecko_,
<a name="line466"></a>        true, this);
<a name="line467"></a>
<a name="line468"></a>    this.eventRegister.listen(outerDoc,
<a name="line469"></a>        &#39;DOMAttrModified&#39;,
<a name="line470"></a>        goog.bind(this.handleDomAttrChange, this, this.handleOuterDocChange_),
<a name="line471"></a>        true);
<a name="line472"></a>  }
<a name="line473"></a>};
<a name="line474"></a>
<a name="line475"></a>
<a name="line476"></a>/** @override */
<a name="line477"></a>goog.editor.SeamlessField.prototype.handleChange = function() {
<a name="line478"></a>  if (this.isEventStopped(goog.editor.Field.EventType.CHANGE)) {
<a name="line479"></a>    return;
<a name="line480"></a>  }
<a name="line481"></a>
<a name="line482"></a>  goog.editor.SeamlessField.superClass_.handleChange.call(this);
<a name="line483"></a>
<a name="line484"></a>  if (this.usesIframe()) {
<a name="line485"></a>    this.sizeIframeToBodyHeightGecko_();
<a name="line486"></a>  }
<a name="line487"></a>};
<a name="line488"></a>
<a name="line489"></a>
<a name="line490"></a>/** @override */
<a name="line491"></a>goog.editor.SeamlessField.prototype.dispatchBlur = function() {
<a name="line492"></a>  if (this.isEventStopped(goog.editor.Field.EventType.BLUR)) {
<a name="line493"></a>    return;
<a name="line494"></a>  }
<a name="line495"></a>
<a name="line496"></a>  goog.editor.SeamlessField.superClass_.dispatchBlur.call(this);
<a name="line497"></a>
<a name="line498"></a>  // Clear the selection and restore the current range back after collapsing
<a name="line499"></a>  // it. The ideal solution would have been to just leave the range intact; but
<a name="line500"></a>  // when there are multiple fields present on the page, its important that
<a name="line501"></a>  // the selection isn&#39;t retained when we switch between the fields. We also
<a name="line502"></a>  // have to make sure that the cursor position is retained when we tab in and
<a name="line503"></a>  // out of a field and our approach addresses both these issues.
<a name="line504"></a>  // Another point to note is that we do it on a setTimeout to allow for
<a name="line505"></a>  // DOM modifications on blur. Otherwise, something like setLoremIpsum will
<a name="line506"></a>  // leave a blinking cursor in the field even though it&#39;s blurred.
<a name="line507"></a>  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE &amp;&amp;
<a name="line508"></a>      !goog.editor.BrowserFeature.CLEARS_SELECTION_WHEN_FOCUS_LEAVES) {
<a name="line509"></a>    var win = this.getEditableDomHelper().getWindow();
<a name="line510"></a>    var dragging = false;
<a name="line511"></a>    goog.events.unlistenByKey(this.listenForDragOverEventKey_);
<a name="line512"></a>    this.listenForDragOverEventKey_ = goog.events.listenOnce(
<a name="line513"></a>        win.document.body, &#39;dragover&#39;,
<a name="line514"></a>        function() {
<a name="line515"></a>          dragging = true;
<a name="line516"></a>        });
<a name="line517"></a>    goog.global.setTimeout(goog.bind(function() {
<a name="line518"></a>      // Do not clear the selection if we&#39;re only dragging text.
<a name="line519"></a>      // This addresses a bug on FF1.5/linux where dragging fires a blur,
<a name="line520"></a>      // but clearing the selection confuses Firefox&#39;s drag-and-drop
<a name="line521"></a>      // implementation. For more info, see http://b/1061064
<a name="line522"></a>      if (!dragging) {
<a name="line523"></a>        if (this.editableDomHelper) {
<a name="line524"></a>          var rng = this.getRange();
<a name="line525"></a>
<a name="line526"></a>          // If there are multiple fields on a page, we need to make sure that
<a name="line527"></a>          // the selection isn&#39;t retained when we switch between fields. We
<a name="line528"></a>          // could have collapsed the range but there is a bug in GECKO where
<a name="line529"></a>          // the selection stays highlighted even though its backing range is
<a name="line530"></a>          // collapsed (http://b/1390115). To get around this, we clear the
<a name="line531"></a>          // selection and restore the collapsed range back in. Restoring the
<a name="line532"></a>          // range is important so that the cursor stays intact when we tab out
<a name="line533"></a>          // and into a field (See http://b/1790301 for additional details on
<a name="line534"></a>          // this).
<a name="line535"></a>          var iframeWindow = this.editableDomHelper.getWindow();
<a name="line536"></a>          goog.dom.Range.clearSelection(iframeWindow);
<a name="line537"></a>
<a name="line538"></a>          if (rng) {
<a name="line539"></a>            rng.collapse(true);
<a name="line540"></a>            rng.select();
<a name="line541"></a>          }
<a name="line542"></a>        }
<a name="line543"></a>      }
<a name="line544"></a>    }, this), 0);
<a name="line545"></a>  }
<a name="line546"></a>};
<a name="line547"></a>
<a name="line548"></a>
<a name="line549"></a>/** @override */
<a name="line550"></a>goog.editor.SeamlessField.prototype.turnOnDesignModeGecko = function() {
<a name="line551"></a>  goog.editor.SeamlessField.superClass_.turnOnDesignModeGecko.call(this);
<a name="line552"></a>  var doc = this.getEditableDomHelper().getDocument();
<a name="line553"></a>
<a name="line554"></a>  doc.execCommand(&#39;enableInlineTableEditing&#39;, false, &#39;false&#39;);
<a name="line555"></a>  doc.execCommand(&#39;enableObjectResizing&#39;, false, &#39;false&#39;);
<a name="line556"></a>};
<a name="line557"></a>
<a name="line558"></a>
<a name="line559"></a>/** @override */
<a name="line560"></a>goog.editor.SeamlessField.prototype.installStyles = function() {
<a name="line561"></a>  if (!this.usesIframe()) {
<a name="line562"></a>    if (!goog.editor.SeamlessField.haveInstalledCss_) {
<a name="line563"></a>      if (this.cssStyles) {
<a name="line564"></a>        goog.style.installStyles(this.cssStyles, this.getElement());
<a name="line565"></a>      }
<a name="line566"></a>
<a name="line567"></a>      // TODO(user): this should be reset to false when the editor is quit.
<a name="line568"></a>      // In non-iframe mode, CSS styles should only be instaled once.
<a name="line569"></a>      goog.editor.SeamlessField.haveInstalledCss_ = true;
<a name="line570"></a>    }
<a name="line571"></a>  }
<a name="line572"></a>};
<a name="line573"></a>
<a name="line574"></a>
<a name="line575"></a>/** @override */
<a name="line576"></a>goog.editor.SeamlessField.prototype.makeEditableInternal = function(
<a name="line577"></a>    opt_iframeSrc) {
<a name="line578"></a>  if (this.usesIframe()) {
<a name="line579"></a>    goog.editor.SeamlessField.superClass_.makeEditableInternal.call(this,
<a name="line580"></a>        opt_iframeSrc);
<a name="line581"></a>  } else {
<a name="line582"></a>    var field = this.getOriginalElement();
<a name="line583"></a>    if (field) {
<a name="line584"></a>      this.setupFieldObject(field);
<a name="line585"></a>      field.contentEditable = true;
<a name="line586"></a>
<a name="line587"></a>      this.injectContents(field.innerHTML, field);
<a name="line588"></a>
<a name="line589"></a>      this.handleFieldLoad();
<a name="line590"></a>    }
<a name="line591"></a>  }
<a name="line592"></a>};
<a name="line593"></a>
<a name="line594"></a>
<a name="line595"></a>/** @override */
<a name="line596"></a>goog.editor.SeamlessField.prototype.handleFieldLoad = function() {
<a name="line597"></a>  if (this.usesIframe()) {
<a name="line598"></a>    // If the CSS inheriting code screws up (e.g. makes fonts too large) and
<a name="line599"></a>    // the field is sized off in goog.editor.Field.makeIframeField, then we need
<a name="line600"></a>    // to size it correctly, but it needs to be visible for the browser
<a name="line601"></a>    // to have fully rendered it. We need to put this on a timeout to give
<a name="line602"></a>    // the browser time to render.
<a name="line603"></a>    var self = this;
<a name="line604"></a>    goog.global.setTimeout(function() {
<a name="line605"></a>      self.doFieldSizingGecko();
<a name="line606"></a>    }, 0);
<a name="line607"></a>  }
<a name="line608"></a>  goog.editor.SeamlessField.superClass_.handleFieldLoad.call(this);
<a name="line609"></a>};
<a name="line610"></a>
<a name="line611"></a>
<a name="line612"></a>/** @override */
<a name="line613"></a>goog.editor.SeamlessField.prototype.getIframeAttributes = function() {
<a name="line614"></a>  return { &#39;frameBorder&#39;: 0, &#39;style&#39;: &#39;padding:0;&#39; };
<a name="line615"></a>};
<a name="line616"></a>
<a name="line617"></a>
<a name="line618"></a>/** @override */
<a name="line619"></a>goog.editor.SeamlessField.prototype.attachIframe = function(iframe) {
<a name="line620"></a>  this.autoDetectFixedHeight_();
<a name="line621"></a>  var field = this.getOriginalElement();
<a name="line622"></a>  var dh = goog.dom.getDomHelper(field);
<a name="line623"></a>
<a name="line624"></a>  // Grab the width/height values of the field before modifying any CSS
<a name="line625"></a>  // as some of the modifications affect its size (e.g. innerHTML=&#39;&#39;)
<a name="line626"></a>  // Here, we set the size of the field to fixed so there&#39;s not too much
<a name="line627"></a>  // jiggling when we set the innerHTML of the field.
<a name="line628"></a>  var oldWidth = field.style.width;
<a name="line629"></a>  var oldHeight = field.style.height;
<a name="line630"></a>  goog.style.setStyle(field, &#39;visibility&#39;, &#39;hidden&#39;);
<a name="line631"></a>
<a name="line632"></a>  // If there is a floated element at the bottom of the field,
<a name="line633"></a>  // then it needs a clearing div at the end to cause the clientHeight
<a name="line634"></a>  // to contain the entire field.
<a name="line635"></a>  // Also, with css re-writing, the margins of the first/last
<a name="line636"></a>  // paragraph don&#39;t seem to get included in the clientHeight. Specifically,
<a name="line637"></a>  // the extra divs below force the field&#39;s clientHeight to include the
<a name="line638"></a>  // margins on the first and last elements contained within it.
<a name="line639"></a>  var startDiv = dh.createDom(goog.dom.TagName.DIV,
<a name="line640"></a>      {&#39;style&#39;: &#39;height:0;clear:both&#39;, &#39;innerHTML&#39;: &#39;&amp;nbsp;&#39;});
<a name="line641"></a>  var endDiv = startDiv.cloneNode(true);
<a name="line642"></a>  field.insertBefore(startDiv, field.firstChild);
<a name="line643"></a>  goog.dom.appendChild(field, endDiv);
<a name="line644"></a>
<a name="line645"></a>  var contentBox = goog.style.getContentBoxSize(field);
<a name="line646"></a>  var width = contentBox.width;
<a name="line647"></a>  var height = contentBox.height;
<a name="line648"></a>
<a name="line649"></a>  var html = &#39;&#39;;
<a name="line650"></a>  if (this.isFixedHeight()) {
<a name="line651"></a>    html = &#39;&amp;nbsp;&#39;;
<a name="line652"></a>
<a name="line653"></a>    goog.style.setStyle(field, &#39;position&#39;, &#39;relative&#39;);
<a name="line654"></a>    goog.style.setStyle(field, &#39;overflow&#39;, &#39;visible&#39;);
<a name="line655"></a>
<a name="line656"></a>    goog.style.setStyle(iframe, &#39;position&#39;, &#39;absolute&#39;);
<a name="line657"></a>    goog.style.setStyle(iframe, &#39;top&#39;, &#39;0&#39;);
<a name="line658"></a>    goog.style.setStyle(iframe, &#39;left&#39;, &#39;0&#39;);
<a name="line659"></a>  }
<a name="line660"></a>  goog.style.setSize(field, width, height);
<a name="line661"></a>
<a name="line662"></a>  // In strict mode, browsers put blank space at the bottom and right
<a name="line663"></a>  // if a field when it has an iframe child, to fill up the remaining line
<a name="line664"></a>  // height. So make the line height = 0.
<a name="line665"></a>  if (goog.editor.node.isStandardsMode(field)) {
<a name="line666"></a>    this.originalFieldLineHeight_ = field.style.lineHeight;
<a name="line667"></a>    goog.style.setStyle(field, &#39;lineHeight&#39;, &#39;0&#39;);
<a name="line668"></a>  }
<a name="line669"></a>
<a name="line670"></a>  goog.editor.node.replaceInnerHtml(field, html);
<a name="line671"></a>  // Set the initial size
<a name="line672"></a>  goog.style.setSize(iframe, width, height);
<a name="line673"></a>  goog.style.setSize(field, oldWidth, oldHeight);
<a name="line674"></a>  goog.style.setStyle(field, &#39;visibility&#39;, &#39;&#39;);
<a name="line675"></a>  goog.dom.appendChild(field, iframe);
<a name="line676"></a>
<a name="line677"></a>  // Only write if its not IE HTTPS in which case we&#39;re waiting for load.
<a name="line678"></a>  if (!this.shouldLoadAsynchronously()) {
<a name="line679"></a>    var doc = iframe.contentWindow.document;
<a name="line680"></a>    if (goog.editor.node.isStandardsMode(iframe.ownerDocument)) {
<a name="line681"></a>      doc.open();
<a name="line682"></a>      doc.write(&#39;&lt;!DOCTYPE HTML&gt;&lt;html&gt;&lt;/html&gt;&#39;);
<a name="line683"></a>      doc.close();
<a name="line684"></a>    }
<a name="line685"></a>  }
<a name="line686"></a>};
<a name="line687"></a>
<a name="line688"></a>
<a name="line689"></a>/** @override */
<a name="line690"></a>goog.editor.SeamlessField.prototype.getFieldFormatInfo = function(
<a name="line691"></a>    extraStyles) {
<a name="line692"></a>  var originalElement = this.getOriginalElement();
<a name="line693"></a>  if (originalElement) {
<a name="line694"></a>    return new goog.editor.icontent.FieldFormatInfo(
<a name="line695"></a>        this.id,
<a name="line696"></a>        goog.editor.node.isStandardsMode(originalElement),
<a name="line697"></a>        true,
<a name="line698"></a>        this.isFixedHeight(),
<a name="line699"></a>        extraStyles);
<a name="line700"></a>  }
<a name="line701"></a>  throw Error(&#39;no field&#39;);
<a name="line702"></a>};
<a name="line703"></a>
<a name="line704"></a>
<a name="line705"></a>/** @override */
<a name="line706"></a>goog.editor.SeamlessField.prototype.writeIframeContent = function(
<a name="line707"></a>    iframe, innerHtml, extraStyles) {
<a name="line708"></a>  // For seamless iframes, hide the iframe while we&#39;re laying it out to
<a name="line709"></a>  // prevent the flicker.
<a name="line710"></a>  goog.style.setStyle(iframe, &#39;visibility&#39;, &#39;hidden&#39;);
<a name="line711"></a>  var formatInfo = this.getFieldFormatInfo(extraStyles);
<a name="line712"></a>  var styleInfo = new goog.editor.icontent.FieldStyleInfo(
<a name="line713"></a>      this.getOriginalElement(),
<a name="line714"></a>      this.cssStyles + this.getIframeableCss());
<a name="line715"></a>  goog.editor.icontent.writeNormalInitialBlendedIframe(
<a name="line716"></a>      formatInfo, innerHtml, styleInfo, iframe);
<a name="line717"></a>  this.doFieldSizingGecko();
<a name="line718"></a>  goog.style.setStyle(iframe, &#39;visibility&#39;, &#39;visible&#39;);
<a name="line719"></a>};
<a name="line720"></a>
<a name="line721"></a>
<a name="line722"></a>/** @override */
<a name="line723"></a>goog.editor.SeamlessField.prototype.restoreDom = function() {
<a name="line724"></a>  // TODO(user): Consider only removing the iframe if we are
<a name="line725"></a>  // restoring the original node.
<a name="line726"></a>  if (this.usesIframe()) {
<a name="line727"></a>    goog.dom.removeNode(this.getEditableIframe());
<a name="line728"></a>  }
<a name="line729"></a>};
<a name="line730"></a>
<a name="line731"></a>
<a name="line732"></a>/** @override */
<a name="line733"></a>goog.editor.SeamlessField.prototype.clearListeners = function() {
<a name="line734"></a>  goog.events.unlistenByKey(this.listenForDragOverEventKey_);
<a name="line735"></a>  goog.events.unlistenByKey(this.listenForIframeLoadEventKey_);
<a name="line736"></a>
<a name="line737"></a>  goog.base(this, &#39;clearListeners&#39;);
<a name="line738"></a>};
</pre>


</body>
</html>
